package com.tmt.helper;

import com.tmt.annotation.MustUpdate;

import java.beans.Transient;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by ThreeManTeam on 2017/6/10.
 */

public class CopyHelper {

    /**
     * 对象拷贝：
     * 1. 如果是非列表属性，则直接使用新值赋值；
     * 2. 如果是列表类型的属性，且idAnnotationClazz不为空，
     * 3. 则从target对象的列表属性中移除在src列表属性中不存在的项，
     * 并把src对象的列表属性中存在但target对象的列表属性中不存在的项，加入到target对象的列表中。
     *
     * @param src
     * @param target
     * @param idAnnotationClazz 列表项
     * @param excludeProperties 不需要拷贝的属性名称数组。
     * @param excludeClasses    不需要拷贝的类型数组。
     */
    public static void copyPropertyValueIfNotNull(Object src, Object target, Class<?> idAnnotationClazz,
                                                  String[] excludeProperties, Class<?>[] excludeClasses) {
        if (src.equals(target))
            return;

        List<Field> validFields = ReflectHelper.findAllDeclaredFields(src.getClass()).stream()
                .filter(x -> x.getAnnotation(Transient.class) == null)
                .collect(Collectors.toList());

        //获取字段上的@MustUpdate注解。
        List<Field> mustUpdateFields = ReflectHelper.findAllFieldByAnnotation(src.getClass(), MustUpdate.class, -1);
        List<String> mustUpdateFieldNames = mustUpdateFields.stream()
                .map(x -> StringHelper.upperFirst(x.getName()))
                .collect(Collectors.toList());

        List<Method> methods = Arrays.asList(src.getClass().getMethods());
        for (Field x : validFields) {
            try {
                String propertyName = StringHelper.upperFirst(x.getName());
                Method getter = methods.stream().filter(y ->
                        y.getName().equals("get" + propertyName)
                                || y.getName().equals("is" + propertyName)).findFirst().orElse(null);
                if (getter != null) {
                    if (excludeProperties != null && Arrays.stream(excludeProperties).anyMatch(y -> Objects.equals(y, propertyName)))
                        continue;

                    if (excludeClasses != null && Arrays.stream(excludeClasses).anyMatch(y -> y.isAssignableFrom(getter.getReturnType())))
                        continue;

                    MustUpdate annotation = null;
                    if (mustUpdateFieldNames.contains(propertyName)) {
                        annotation = ReflectHelper.findFieldByName(src.getClass(), StringHelper.lowerFirst(propertyName)).getAnnotation(MustUpdate.class);
                    }

                    if (Collection.class.isAssignableFrom(getter.getReturnType())
                            && idAnnotationClazz != null
                            && (annotation == null || annotation.mergeMode())) {
                        Collection srcList = (Collection) getter.invoke(src);
                        Collection tarList = (Collection) getter.invoke(target);

                        if (srcList != null && tarList != null) {
                            //先从列表中移除废弃的资料记录。
                            List removedList = new ArrayList<>();
                            tarList.stream().forEach(y -> {
                                if (srcList == null || srcList.stream().noneMatch(z -> {
                                    Long zId = (Long) ReflectHelper.getFieldValueByAnnotation(z, idAnnotationClazz, null);
                                    Long yId = (Long) ReflectHelper.getFieldValueByAnnotation(y, idAnnotationClazz, null);
                                    //说明列表项不是实体对象，则按照值类型进行出来。
                                    return (zId == null && yId == null) ? Objects.equals(z, y) : Objects.equals(zId, yId);
                                })) {
                                    removedList.add(y);
                                }
                            });
                            tarList.removeAll(removedList);

                            //再将新增对象添加到列表中。
                            List addedList = new ArrayList();
                            srcList.stream().forEach(y -> {
                                if (tarList.stream().noneMatch(z -> {
                                    Long zId = (Long) ReflectHelper.getFieldValueByAnnotation(z, idAnnotationClazz, null);
                                    Long yId = (Long) ReflectHelper.getFieldValueByAnnotation(y, idAnnotationClazz, null);
                                    //说明列表项不是实体对象，则按照值类型进行出来。
                                    return (zId == null && yId == null) ? Objects.equals(z, y) : Objects.equals(zId, yId);

                                })) {
                                    addedList.add(y);
                                }
                            });
                            tarList.addAll(addedList);
                        }
                    } else {
                        //更新target对象的属性值。
                        Object value = getter.invoke(src);
                        if (value != null || mustUpdateFieldNames.contains(propertyName)) {
                            Method setter = methods.stream().filter(y -> y.getName().equals("set" + propertyName)).findFirst().orElse(null);
                            if (setter != null) {
                                setter.invoke(target, value);
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}
